package com.hanliy.service;

import cn.hutool.extra.servlet.ServletUtil;
import com.github.binarywang.wxpay.service.WxPayService;
import com.hanliy.config.WxProperties;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.model.OrderQueryModel;
import com.ijpay.wxpay.model.RefundModel;
import com.ijpay.wxpay.model.RefundQueryModel;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author hanyulin
 * @apiNote
 * @date 2021/12/16 16:53
 */
@Service
@Slf4j
public class PayService {
    private final Log logger = LogFactory.getLog(WxPayService.class);

    @Autowired
    private WxProperties wxProperties;

    /**
     * 付款订单的预支付会话标识
     * <p> 此接口对应的是 V2 的版本
     * 1. 检查订单是否能够付款
     * 2. 微信商户平台返回支付订单ID
     * 3. 设置订单付款状态
     *
     * @param originType 订单来源(0小程序|1安卓|2IOS)
     * @return java.lang.Object
     * @author hanyulin
     * @date 2021/12/22 9:50
     */
    public Object prepay(Integer originType) {
        UnifiedOrderModel.UnifiedOrderModelBuilder unifiedOrderModelBuilder = UnifiedOrderModel.builder();
        // 商户号
        unifiedOrderModelBuilder.mch_id(wxProperties.getPay().getMchId());
        // 商品描述: 订单编号
        unifiedOrderModelBuilder.body("");
        // 订单编号：订单中获取
        unifiedOrderModelBuilder.out_trade_no("");
        // 价格单位:(分) 整数
        unifiedOrderModelBuilder.total_fee("");
        // 附加数据
        unifiedOrderModelBuilder.attach("");
        // 随机字符串
        unifiedOrderModelBuilder.nonce_str(WxPayKit.generateStr());
        //支付的ip地址
        unifiedOrderModelBuilder.spbill_create_ip(ServletUtil.getClientIP(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()));
        // 支付回调地址
        unifiedOrderModelBuilder.notify_url(wxProperties.getPay().getDoMain() + "/other/wx/notifyUrl");
        if (originType == 0) {
            // 小程序appId
            unifiedOrderModelBuilder.appid(wxProperties.getMiniApp().getAppId());
            // 小程序中的openId（ios以及android是没有的）
            unifiedOrderModelBuilder.openid("");
            // 交易方式
            unifiedOrderModelBuilder.trade_type(TradeType.JSAPI.getTradeType());
        } else if (originType == 1) {
            // 安卓appId
            unifiedOrderModelBuilder.appid(wxProperties.getAndroid().getAppId());
            // 交易方式
            unifiedOrderModelBuilder.trade_type(TradeType.APP.getTradeType());
        } else if (originType == 2) {
            // ios的appId
            unifiedOrderModelBuilder.appid(wxProperties.getIos().getAppId());
            // 交易方式
            unifiedOrderModelBuilder.trade_type(TradeType.APP.getTradeType());
        }
        Map<String, String> requestParams = unifiedOrderModelBuilder.build().createSign(wxProperties.getPay().getMchSecret(), SignType.MD5);
        logger.info("统一下单请求参数：" + requestParams);
        // 向微信发送统一下单请求
        String resultXml = WxPayApi.pushOrder(false, requestParams);
        logger.info("统一下单请求参数：" + resultXml);
        // 将获取结果集xml变为Map
        Map<String, String> resultMap = WxPayKit.xmlToMap(resultXml);
        String returnCode = resultMap.get("return_code");
        String returnMsg = resultMap.get("return_msg");
        // 检查是否连接到微信支付
        if (!WxPayKit.codeIsOk(returnCode)) {
            return returnMsg;
        }
        // 检查交易是否成功
        String resultCode = resultMap.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            return returnMsg;
        }
        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
        Map<String, String> returnParams = null;
        if (originType == 0) {
            returnParams = WxPayKit.miniAppPrepayIdCreateSign(wxProperties.getMiniApp().getAppId(), resultMap.get("prepay_id"), wxProperties.getPay().getMchSecret(), SignType.MD5);
            logger.info("小程序调起支付的参数: " + returnParams);
        } else if (originType == 1) {
            returnParams = WxPayKit.appPrepayIdCreateSign(wxProperties.getAndroid().getAppId(), wxProperties.getPay().getMchId(), resultMap.get("prepay_id"), wxProperties.getPay().getMchSecret(), SignType.MD5);
            logger.info("Android调起支付的参数: " + returnParams);
        } else if (originType == 2) {
            returnParams = WxPayKit.appPrepayIdCreateSign(wxProperties.getIos().getAppId(), wxProperties.getPay().getMchId(), resultMap.get("prepay_id"), wxProperties.getPay().getMchSecret(), SignType.MD5);
            logger.info("IOS调起支付的参数: " + returnParams);
        }
        /*
         * 数据库处理：设置状态为未支付、设置支付方式等业务处理
         */
        return returnParams;
    }

    /**
     * 支付回调地址
     *
     * @param request  请求内容
     * @return java.lang.Object
     * @author hanyulin
     * @date 2021/12/22 16:22
     */
    public Object payNotify(HttpServletRequest request) {
        Map<String, String> analysisMap = WxPayKit.xmlToMap(HttpKit.readData(request));
        logger.info("支付结果通知：" + analysisMap);
        // 验证支付
        if (WxPayKit.verifyNotify(analysisMap, wxProperties.getPay().getMchSecret(), SignType.MD5)) {

            // 与微信通讯是否成功
            if (WxPayKit.codeIsOk(analysisMap.get("return_code"))) {
                Map<String, String> resultParams = new HashMap<>();
                resultParams.put("return_code", "SUCCESS");
                resultParams.put("return_msg", "OK");

                //支付成功
                if (WxPayKit.codeIsOk(analysisMap.get("result_code"))) {

                    // ！得到的订单编号中能否查询相应的订单 ！

                    // ！校验返回的订单金额是否与商户的订单金额一致 ！

                    // ！该通知是否已经处理过 ！

                    /*
                     * 数据库的业务处理
                     */
                    return WxPayKit.toXml(resultParams);
                } else {
                    // 交易失败
                    // ! 用户输错密码 银行卡余额不足 不走微信支付结果通知接口的失败 !
                    log.info("微信支付交易失败");
                    return WxPayKit.toXml(resultParams);
                }
            }else {
                log.info("微信回调：微信通讯失败: {}", analysisMap.get("return_msg"));
            }
        }
        log.info("非法微信回调");
        return null;

    }

    /**
     * 订单退款
     * <p> 由订单的支付方式再调起哪种退款
     * 1. 该订单是否能够退款
     * 2. 修改订单状态
     *
     * @param orderNo  订单编号
     * @param originType  订单来源
     * @return java.lang.Object
     * @author hanyulin
     * @date 2021/12/24 14:00
     */
    public Object refund(String orderNo,Integer originType) {
        /*
         * 查看能否搜寻到该订单
         */

        /*
         * 查看订单是否能够退款
         */

        // 微信退款
        String appid = null;
        // 0.小程序  1.安卓  2.iOS
        if (originType == 0) {
            appid = wxProperties.getMiniApp().getAppId();
        } else if (originType == 1) {
            appid = wxProperties.getAndroid().getAppId();
        } else {
            appid = wxProperties.getIos().getAppId();
        }
        Map<String, String> params = RefundModel.builder()
                .appid(appid)
                .mch_id(wxProperties.getPay().getMchId())
                .nonce_str(WxPayKit.generateStr())
                .out_trade_no(orderNo)
                .out_refund_no("R" + orderNo.substring(1))
                .total_fee("")
                .refund_fee("")
                .notify_url(wxProperties.getPay().getDoMain() + "/wx/refundUrl")
                .build()
                .createSign(wxProperties.getPay().getMchSecret(), SignType.MD5);
        Map<String, String> resultMap = null;
        try {
            resultMap = WxPayKit.xmlToMap(WxPayApi.orderRefund(false, params, new ClassPathResource("apiclient_cert.p12").getURL().getPath(), wxProperties.getPay().getMchId()));
        } catch (IOException e) {
            return new Exception("微信调起支付退款异常" + e.getMessage());
        }
        String returnCode = resultMap.get("return_code");
        String returnMsg = resultMap.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            log.info("微信退款失败：{}", returnMsg);
            return "微信退款失败";
        }
        log.info("微信退款成功");
        return "微信退款成功";
    }

    /**
     * 微信支付的退款回调
     *
     * @param request 请求内容
     * @author hanyulin
     * @date 2022/1/10 15:05
     * @return java.lang.String
     */
    public String refundUrl(HttpServletRequest request){
        String xmlMsg = HttpKit.readData(request);
        Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
        log.info("退款通知：{}", params);

        String returnCode = params.get("return_code");
        if (WxPayKit.codeIsOk(returnCode)) {
            String reqInfo = params.get("req_info");
            Map<String, String> decryptData = WxPayKit.xmlToMap(WxPayKit.decryptData(reqInfo, wxProperties.getPay().getMchSecret()));
            log.info("退款通知解密后的数据：{}", decryptData);

            Map<String, String> xml = new HashMap<String, String>(2);
            xml.put("return_code", "SUCCESS");
            xml.put("return_msg", "OK");
            // 退款是否成功
            if (WxPayKit.codeIsOk(decryptData.get("refund_status"))) {
                /*
                 * 当收到通知进行处理时，首先检查对应业务数据的状态，判断该通知是否已经处理过，
                 * 如果没有处理过再进行处理，如果处理过直接返回结果成功。
                 */
                return WxPayKit.toXml(xml);
            }else {
                if (("CHANGE".equals(decryptData.get("refund_status")))) {
                    /*
                     * “退款异常”：当用户使用银行卡支付时，当银行卡状态不正常或银行卡错误时，又退回用户微信零钱失败
                     */
                    log.info("微信退款发生退款异常");
                } else {
                    /*
                     * “退款关闭”：余额不足或者其他情况
                     */
                    log.info("微信退款发生退款关闭异常");
                }
                return WxPayKit.toXml(xml);
            }
        }log.info("微信退款回调错误：{}", params.get("return_msg"));
        return null;
    }

    /**
     * 微信支付订单的查询
     *
     * @param orderNo    订单号
     * @param originType 订单来源 0.小程序  1.安卓  2.iOS
     */
    public Map<String, String> orderQuery(String orderNo, Integer originType) {
        String appid = null;
        // 0.小程序  1.安卓  2.iOS
        if (originType == 0) {
            appid = wxProperties.getMiniApp().getAppId();
        } else if (originType == 1) {
            appid = wxProperties.getAndroid().getAppId();
        } else {
            appid = wxProperties.getIos().getAppId();
        }
        Map<String, String> params = OrderQueryModel.builder()
                .appid(appid)
                .mch_id(wxProperties.getPay().getMchId())
                .out_trade_no(orderNo)
                .nonce_str(WxPayKit.generateStr())
                .build()
                .createSign(wxProperties.getPay().getMchSecret(), SignType.MD5);
        Map<String, String> resultMap = WxPayKit.xmlToMap(WxPayApi.orderQuery(params));
        String return_code = resultMap.get("return_code");
        String return_msg = resultMap.get("return_msg");
        if (!WxPayKit.codeIsOk(return_code)) {
            log.info("查询微信支付订单失败：{}", return_msg);
        }
        log.info("查询微信支付订单：{}", resultMap);
        return resultMap;
    }

    /**
     * 微信退款订单的查询
     *
     * @param orderNo    订单号
     * @param originType 订单来源 0.小程序  1.安卓  2.iOS
     */
    public Map<String, String> refundQuery(String orderNo, Integer originType) {
        String appid = null;
        // 0.小程序  1.安卓  2.iOS
        if (originType == 0) {
            appid = wxProperties.getMiniApp().getAppId();
        } else if (originType == 1) {
            appid = wxProperties.getAndroid().getAppId();
        } else {
            appid = wxProperties.getIos().getAppId();
        }
        Map<String, String> params = RefundQueryModel.builder()
                .appid(appid)
                .mch_id(wxProperties.getPay().getMchId())
                .out_trade_no(orderNo)
                .nonce_str(WxPayKit.generateStr())
                .build()
                .createSign(wxProperties.getPay().getMchSecret(), SignType.MD5);
        Map<String, String> resultMap = WxPayKit.xmlToMap(WxPayApi.orderRefundQuery(false, params));
        String returnCode = resultMap.get("return_code");
        String returnMsg = resultMap.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            log.info("查询退款信息失败：{}", returnMsg);
        }
        log.info("查询退款信息：{}", resultMap);
        return resultMap;
    }
}
